package me.panpf.sketch.sample.widget.page;
/*
 * Copyright (C) 2021 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
import me.panpf.sketch.SketchImageView;
import me.panpf.sketch.drawable.SketchLoadingDrawable;
import me.panpf.sketch.util.Log;
import me.panpf.sketch.util.SketchUtils;
import me.panpf.sketch.zoom.BlockDisplayer;
import me.panpf.sketch.zoom.RectF;
import me.panpf.sketch.zoom.Size;
import me.panpf.sketch.zoom.block.Block;
import me.panpf.sketch.zoom.scroller.GestureDetector;
import ohos.agp.colors.RgbColor;
import ohos.agp.components.AttrSet;
import ohos.agp.components.Component;
import ohos.agp.components.ComponentContainer;
import ohos.agp.render.Canvas;
import ohos.agp.render.Paint;
import ohos.agp.render.Path;
import ohos.agp.utils.*;
import ohos.agp.window.service.DisplayManager;
import ohos.app.Context;
import ohos.multimodalinput.event.MmiPoint;
import ohos.multimodalinput.event.TouchEvent;

/**
 * MappingView
 */
public class MappingView extends SketchImageView {

    private BlockDisplayer depBlockDisplayer = null;

    private Rect visibleMappingRect = null;
    private Paint visiblePaint = null;
    private Paint drawBlockPaint = null;
    private Paint realSrcRectPaint = null;
    private Paint originSrcRectPaint = null;
    private Paint loadingBlockPaint = null;

    private Size drawableSize = new Size();
    private Rect visibleRect = new Rect();

    private GestureDetector detector = null;

    public MappingView(Context context) {
        this(context, null);
    }

    public MappingView(Context context, AttrSet attrs) {
        this(context, attrs, 0);
    }

    public MappingView(Context context, AttrSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        init(context);
    }


    private void init(Context context) {
        visibleMappingRect = new Rect();
        visiblePaint = new Paint();
        visiblePaint.setColor(Color.RED);
        visiblePaint.setStyle(Paint.Style.STROKE_STYLE);
        visiblePaint.setStrokeWidth(SketchUtils.dp2px(context, 1));

        drawBlockPaint = new Paint();
        drawBlockPaint.setColor(new Color(0x88A020F0));
        drawBlockPaint.setStrokeWidth(SketchUtils.dp2px(context, 1));
        drawBlockPaint.setStyle(Paint.Style.STROKE_STYLE);

        loadingBlockPaint = new Paint();
        loadingBlockPaint.setColor(new Color(0x880000CD));
        loadingBlockPaint.setStrokeWidth(SketchUtils.dp2px(context, 1));
        loadingBlockPaint.setStyle(Paint.Style.STROKE_STYLE);

        realSrcRectPaint = new Paint();
        realSrcRectPaint.setColor(new Color(0x8800CD00));
        realSrcRectPaint.setStrokeWidth(SketchUtils.dp2px(context, 1));
        realSrcRectPaint.setStyle(Paint.Style.STROKE_STYLE);


        originSrcRectPaint = new Paint();
        originSrcRectPaint.setColor(new Color(0x88FF7F24));
        originSrcRectPaint.setStrokeWidth(SketchUtils.dp2px(context, 1));
        originSrcRectPaint.setStyle(Paint.Style.STROKE_STYLE);
    }


    @Override
    public boolean onEstimateSize(int i0, int i1) {
        boolean isEs = super.onEstimateSize(i0, i1);
        recover();
        return isEs;
    }


    @Override
    public void onDraw(Component component, Canvas canvas) {
        super.onDraw(component, canvas);
        if (depBlockDisplayer != null && depBlockDisplayer.isReady()) {
            float widthScale = depBlockDisplayer.getImageSize().getPointX() / getWidth();
            float heightScale = depBlockDisplayer.getImageSize().getPointY() / getHeight();
            for (Block block : depBlockDisplayer.getBlockList()) {
                if (!block.isEmpty()) {
                    Point leftTop = new Point((block.srcRect.left + 1) / widthScale, (block.srcRect.top + 1) / heightScale);
                    Point rightTop = new Point((block.srcRect.right - 1) / widthScale, (block.srcRect.top + 1) / heightScale);
                    Point leftBottom = new Point((block.srcRect.left + 1) / widthScale, (block.srcRect.bottom - 1) / heightScale);
                    Point rightBottom = new Point((block.srcRect.right - 1) / widthScale, (block.srcRect.bottom - 1) / heightScale);

                    Line line = new Line(leftTop, rightTop);
                    Line line1 = new Line(rightTop, rightBottom);
                    Line line2 = new Line(rightBottom, leftBottom);
                    Line line3 = new Line(leftBottom, leftTop);
                    drawBlockPaint.setStrokeWidth(3);
                    canvas.drawLine(line, drawBlockPaint);
                    canvas.drawLine(line1, drawBlockPaint);
                    canvas.drawLine(line2, drawBlockPaint);
                    canvas.drawLine(line3, drawBlockPaint);

                } else if (!block.isDecodeParamEmpty()) {
                    Point leftTop = new Point((block.srcRect.left + 1) / widthScale, (block.srcRect.top + 1) / heightScale);
                    Point rightTop = new Point((block.srcRect.right - 1) / widthScale, (block.srcRect.top + 1) / heightScale);
                    Point leftBottom = new Point((block.srcRect.left + 1) / widthScale, (block.srcRect.bottom - 1) / heightScale);
                    Point rightBottom = new Point((block.srcRect.right - 1) / widthScale, (block.srcRect.bottom - 1) / heightScale);

                    Line line = new Line(leftTop, rightTop);
                    Line line1 = new Line(rightTop, rightBottom);
                    Line line2 = new Line(rightBottom, leftBottom);
                    Line line3 = new Line(leftBottom, leftTop);
                    loadingBlockPaint.setStrokeWidth(3);
                    canvas.drawLine(line, loadingBlockPaint);
                    canvas.drawLine(line1, loadingBlockPaint);
                    canvas.drawLine(line2, loadingBlockPaint);
                    canvas.drawLine(line3, loadingBlockPaint);
                }
            }
            drawOriginSrcRect(canvas, widthScale, heightScale);
            drawRealSrcRect(canvas, widthScale, heightScale);
        }
        drawVisibleLine(canvas);
    }

    private void drawOriginSrcRect(Canvas canvas, float widthScale, float heightScale) {
        Rect drawSrcRect = depBlockDisplayer.getDrawSrcRect();
        if (!drawSrcRect.isEmpty()) {
            Point leftTop = new Point(drawSrcRect.left / widthScale, drawSrcRect.top / heightScale);
            Point rightTop = new Point(drawSrcRect.right / widthScale, drawSrcRect.top / heightScale);
            Point leftBottom = new Point(drawSrcRect.left / widthScale, drawSrcRect.bottom / heightScale);
            Point rightBottom = new Point(drawSrcRect.right / widthScale, drawSrcRect.bottom / heightScale);

            Line line = new Line(leftTop, rightTop);
            Line line1 = new Line(rightTop, rightBottom);
            Line line2 = new Line(rightBottom, leftBottom);
            Line line3 = new Line(leftBottom, leftTop);
            originSrcRectPaint.setStrokeWidth(3);
            canvas.drawLine(line, originSrcRectPaint);
            canvas.drawLine(line1, originSrcRectPaint);
            canvas.drawLine(line2, originSrcRectPaint);
            canvas.drawLine(line3, originSrcRectPaint);
        }
    }

    private void drawRealSrcRect(Canvas canvas, float widthScale, float heightScale) {
        Rect decodeSrcRect = depBlockDisplayer.getDecodeSrcRect();
        if (!decodeSrcRect.isEmpty()) {
            Line line = new Line(new Point(decodeSrcRect.left / widthScale, decodeSrcRect.top / heightScale), new Point(decodeSrcRect.right / widthScale, decodeSrcRect.top / heightScale));
            Line line1 = new Line(new Point(decodeSrcRect.right / widthScale, decodeSrcRect.top / heightScale), new Point(decodeSrcRect.right / widthScale, decodeSrcRect.bottom / heightScale));
            Line line2 = new Line(new Point(decodeSrcRect.right / widthScale, decodeSrcRect.bottom / heightScale), new Point(decodeSrcRect.left / widthScale, decodeSrcRect.bottom / heightScale));
            Line line3 = new Line(new Point(decodeSrcRect.left / widthScale, decodeSrcRect.bottom / heightScale), new Point(decodeSrcRect.left / widthScale, decodeSrcRect.top / heightScale));
            realSrcRectPaint.setStrokeWidth(3);
            canvas.drawLine(line, realSrcRectPaint);
            canvas.drawLine(line1, realSrcRectPaint);
            canvas.drawLine(line2, realSrcRectPaint);
            canvas.drawLine(line3, realSrcRectPaint);
        }
    }

    private void drawVisibleLine(Canvas canvas) {
        if (!visibleMappingRect.isEmpty()) {
            Line line = new Line(new Point(visibleMappingRect.left, visibleMappingRect.top), new Point(visibleMappingRect.right, visibleMappingRect.top));
            Line line1 = new Line(new Point(visibleMappingRect.right, visibleMappingRect.top), new Point(visibleMappingRect.right, visibleMappingRect.bottom));
            Line line2 = new Line(new Point(visibleMappingRect.right, visibleMappingRect.bottom), new Point(visibleMappingRect.left, visibleMappingRect.bottom));
            Line line3 = new Line(new Point(visibleMappingRect.left, visibleMappingRect.bottom), new Point(visibleMappingRect.left, visibleMappingRect.top));
            realSrcRectPaint.setStrokeWidth(3);
            canvas.drawLine(line, visiblePaint);
            canvas.drawLine(line1, visiblePaint);
            canvas.drawLine(line2, visiblePaint);
            canvas.drawLine(line3, visiblePaint);
        }
    }

    private void recover() {
        Log.info("MappingView.recover([]):138");
        if (!visibleRect.isEmpty()) {
            update(drawableSize, visibleRect);
        }
    }

    /**
     * update
     *
     * @param newDrawableSize
     * @param newVisibleRect
     */
    public void update(Size newDrawableSize, Rect newVisibleRect) {
        Log.info("MappingView.update    newDrawableSize:" + newDrawableSize.toString() + "   newVisibleRect:" + newVisibleRect.toString());
        if (newDrawableSize.getWidth() == 0 || newDrawableSize.getHeight() == 0 || newVisibleRect.isEmpty()) {
            drawableSize.set(0, 0);
            visibleRect.clear();

            if (!visibleMappingRect.isEmpty()) {
                visibleMappingRect.clear();
                invalidate();
            }
            return;
        }

        drawableSize.set(newDrawableSize.getWidth(), newDrawableSize.getHeight());

        visibleRect = new Rect(newVisibleRect);
        Log.info("MappingView.update([newDrawableSize, newVisibleRect]):163  " + isUsableDrawable() + "    " + getWidth() + "   " + getHeight());
        if (!isUsableDrawable() || getEstimatedWidth() == 0 || getEstimatedHeight() == 0) {

            if (!visibleMappingRect.isEmpty()) {
                visibleMappingRect.clear();
                invalidate();
            }

            return;
        }

        if (resetViewSize()) {
            return;
        }

        resetVisibleMappingRect();
        Log.info("MappingView.update([newDrawableSize, newVisibleRect]):179");
        invalidate();
    }

    /**
     * blockChanged
     *
     * @param blockDisplayer
     */
    public void blockChanged(BlockDisplayer blockDisplayer) {
        this.depBlockDisplayer = blockDisplayer;
        getContext().getUITaskDispatcher().asyncDispatch(new Runnable() {
            @Override
            public void run() {
                invalidate();
            }
        });
    }

    /**
     * setOnSingleClickListener
     *
     * @param onSingleClickListener
     */
    public void setOnSingleClickListener(OnSingleClickListener onSingleClickListener) {
        setClickable(onSingleClickListener != null);
        if (detector == null) {
            detector = new GestureDetector(getContext(), new GestureDetector.SimpleOnGestureListener() {

                @Override
                public boolean onSingleTapUp(TouchEvent ex) {
                    MmiPoint mmiPoint = ex.getPointerPosition(ex.getIndex());
                    return onSingleClickListener.onSingleClick(mmiPoint.getX(), mmiPoint.getY());
                }
            });
        }
    }

    /**
     * isUsableDrawable
     *
     * @return boolean
     */
    public boolean isUsableDrawable() {
        return getDrawable() != null && !(getDrawable() instanceof SketchLoadingDrawable);
    }

    private Boolean resetViewSize() {
        if (getDrawable() == null) {
            return true;
        }

        int drawableWidth = getDrawable().getWidth();
        int drawableHeight = getDrawable().getHeight();
        Log.info("MappingView.resetViewSize([]):220   drawableWidth:" + drawableWidth + "   " + drawableHeight);
        int maxWidth;
        int maxHeight;
        int screenWidth = DisplayManager.getInstance().getDefaultDisplay(getContext()).get().getAttributes().width;
        int screenHeight = DisplayManager.getInstance().getDefaultDisplay(getContext()).get().getAttributes().height;
        if (Math.max(drawableWidth, drawableHeight) / Math.min(drawableWidth, drawableHeight) >= 4) {
            maxWidth = Math.round(screenWidth / 2f);
            maxHeight = Math.round(screenHeight / 2f);
        } else {
            maxWidth = Math.round(screenWidth / 4f);
            maxHeight = Math.round(screenHeight / 4f);
        }
        int newViewWidth;
        int newViewHeight;
        if (drawableWidth > maxWidth || drawableHeight > maxHeight) {
            int finalScale = Math.min(maxWidth * 1000 / drawableWidth, maxHeight * 1000 / drawableHeight);
            newViewWidth = Math.round(drawableWidth * finalScale / 1000f);
            newViewHeight = Math.round(drawableHeight * finalScale / 1000f);
        } else {
            newViewWidth = drawableWidth;
            newViewHeight = drawableHeight;
        }
        ComponentContainer.LayoutConfig layoutParams = getLayoutParams();
        Log.info("MappingView.resetViewSize([]):243   设置前的mapviewsize" + newViewWidth + "    " + layoutParams.width);
        if (newViewWidth != layoutParams.width || newViewHeight != layoutParams.height) {
            layoutParams.width = newViewWidth;
            layoutParams.height = newViewHeight;
            setLayoutConfig(layoutParams);
            Log.info("MappingView.resetViewSize([]):243     重置后的mapviewsize:" + newViewWidth + "    " + layoutParams.width);
            return true;
        } else {
            return false;
        }
    }


    private void resetVisibleMappingRect() {
        int selfWidth = getWidth();
        int selfHeight = getHeight();
        float widthScale = (float) ((double) selfWidth / drawableSize.getWidth());
        float heightScale = (float) ((double) selfHeight / drawableSize.getHeight());
        this.visibleMappingRect.set(
                Math.round(visibleRect.left * widthScale),
                Math.round(visibleRect.top * heightScale),
                Math.round(visibleRect.right * widthScale),
                Math.round(visibleRect.bottom * heightScale));
        Log.info("MappingView.resetVisibleMappingRect([]):246  mapview的选中区域框:" + visibleMappingRect);
    }

    /**
     * OnSingleClickListener
     */
    public interface OnSingleClickListener {
        /**
         * onSingleClick
         *
         * @param x1
         * @param y1
         * @return boolean
         */
        public boolean onSingleClick(float x1, float y1);
    }

    @Override
    public boolean onTouchEvent(Component component, TouchEvent touchEvent) {
        if (detector == null) {
            return super.onTouchEvent(component, touchEvent);
        }

        detector.onTouchEvent(touchEvent);
        return true;
    }

}
